package com.aboverock.core.aop;


import com.aboverock.module.system.domain.Log;
import com.aboverock.module.system.domain.User;
import com.aboverock.module.system.service.LogService;
import com.aboverock.module.system.service.UserService;

import com.aboverock.autoconfigure.util.JwtUtil;
import com.aboverock.common.domain.Result;
import com.aboverock.common.enums.BrowserType;
import com.aboverock.common.enums.LogTypeEnum;
import com.aboverock.common.utils.BrowserUtils;
import com.aboverock.common.utils.ClientIpUtils;
import com.aboverock.core.security.annotation.SecurityLog;
import com.aboverock.core.security.enums.SecurityTypeEnum;
import com.aboverock.core.spring.SpringWebHolder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.tomcat.jni.Time;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 使用AspectJ实现用户锁定、解锁、删除日志AOP.
 *
 * @author Rock Wang
 */
@Component
@Aspect
public class SecurityLogAspect {

    private static Logger logger = LoggerFactory.getLogger(SecurityLogAspect.class);

    @Autowired
    private UserService userService;

    @Autowired
    private LogService logService;

    /**
     * 安全日志记录.
     *
     * @param joinPoint the join point
     * @param securityLog the security log
     * @return the object
     * @throws Throwable the throwable
     */
    @Around("@annotation(securityLog)")
    public Object doSecurityLog(ProceedingJoinPoint joinPoint, SecurityLog securityLog)
            throws Throwable {
        return saveLog(joinPoint, securityLog.securityType());
    }

    /**
     * 保存日志.
     *
     * @param joinPoint 切入点
     * @param securityType the security type
     * @return the object
     * @throws Throwable the throwable
     * @see SecurityTypeEnum
     */
    @SuppressWarnings("unchecked")
    public Object saveLog(ProceedingJoinPoint joinPoint, SecurityTypeEnum securityType)
            throws Throwable {

        long startTime = System.currentTimeMillis();
        // 执行方法
        Object result = joinPoint.proceed();

        if (result != null && (Result.SUCCESS_CODE == ((Result) result).getCode())) {
            long end = System.currentTimeMillis();

            Log log = new Log();
            log.setType(LogTypeEnum.SECURITY.getValue());

            String loginName = JwtUtil.getLoginName();
            log.setLoginName(loginName);

            HttpServletRequest request = SpringWebHolder.getRequest();
            String ip = ClientIpUtils.getIpAddr(request);
            log.setIp(ip);

            BrowserType browserType = BrowserUtils.getBrowserType(request);
            log.setBrowserType(null == browserType ? "Unknown" : browserType.toString());

            log.setRequest(request.getRequestURI());
            log.setHttpMethod(request.getMethod());

            log.setRequestTime(new Date(startTime));
            log.setOperationTime(String.valueOf(1.0 * (end - startTime) / Time.APR_MSEC_PER_USEC));

            StringBuilder argsString = new StringBuilder();
            switch (securityType) {
                case USER_LOCK:
                case USER_UNLOCK:
                case USER_DELETE:
                    Object[] args = joinPoint.getArgs();
                    if (args != null) {
                        Object arg = args[0];
                        if (arg instanceof List) {
                            ((List<User>) arg).forEach(user -> {
                                User u = userService.getByIdIgnoreLogicDelete(user.getId());
                                argsString.append(u.getLoginName()).append(" | ");
                            });
                        }
                    }
                    break;
                default:
                    break;
            }
            ObjectMapper mapper = new ObjectMapper();
            ObjectNode object = mapper.createObjectNode();

            JsonNode desc = mapper.readTree(securityType.getDescription());
            Iterator<String> fieldNames = desc.fieldNames();
            while (fieldNames.hasNext()) {
                String key = fieldNames.next();
                object.put(key, desc.get(key).asText() + "：" + argsString.toString());
            }
            log.setDetails(object.toString());
            if (logger.isDebugEnabled()) {
                logger.debug("user:{}, request url：{} ,  time-consuming：{}s, exception：{}.",
                        loginName, log.getRequest(), log.getOperationTime(), log.getDetails());
            }
            logService.save(log);
        }

        return result;
    }
}
